library(plotly)
library(data.table)
library(tidyr)
library(knitr)
library(heatmaply)

Preprocessing

  • Load data file
  • rename genres for better readability
    • “Religion, Spirituality & New Age” to “Religion”
    • “Science.fiction” to “SciFi”
    • “Action.and.Adventure” to “Action”

All genres:

books_mat <- read.csv(file_bookstore, row.names = 1)
rownames(books_mat) <- make.names(rownames(books_mat))
rownames(books_mat) <- sub("Science.fiction", "SciFi", rownames(books_mat))
rownames(books_mat) <- sub("Action.and.Adventure", "Action", rownames(books_mat))
rownames(books_mat) <- sub("Religion..Spirituality...New.Age", 
    "Religion", rownames(books_mat)
)
colnames(books_mat) <- rownames(books_mat)
rownames(books_mat)
 [1] "Satire"        "SciFi"         "Drama"         "Action"        "Romance"       "Mystery"       "Horror"       
 [8] "Self.help"     "Health"        "Guide"         "Travel"        "Children.s"    "Religion"      "Science"      
[15] "History"       "Math"          "Anthology"     "Poetry"        "Encyclopedias" "Dictionaries"  "Comics"       
[22] "Art"           "Cookbooks"     "Diaries"       "Journals"     
  • Check if upper and lower triangle identical
is_upper_lower <- identical(
    books_mat[upper.tri(books_mat)], 
    t(books_mat)[upper.tri(books_mat)]
)
is_upper_lower
[1] TRUE
  • Transform to long and tidy data.table
books_dt <- as.data.table(books_mat, keep.rownames = TRUE)
setnames(books_dt, c('genreA',colnames(books_mat)))
books_dt <- as.data.table(gather(books_dt, genreB, customers, Satire:Journals))
head(books_dt)
  • Average number of genres per customer
sum(books_dt[genreA==genreB, customers])/total_customers
[1] 2.332187

First ideas

Show me everything!

hm <- heatmapr(books_mat)
heatmaply(hm, 
    plot_method = 'plotly', 
    colors =  c('grey95', 'dodgerblue')
)
  • Romance, SciFi, Action, History are most bought
  • bought-together clusters:
    • Romance, SciFi, Action, History
    • Dictionaries and Comics
    • Math and Poetry
  • Mystery is an outlier

Most bought genre

plot_ly(data=books_dt[genreA==genreB][order(customers)], 
    x=~genreA, y=~customers, type="bar"
)%>% layout(
    margin=list(b=100), 
    xaxis=list(categoryorder="trace"),
    title="Most bought genre"
)

Best pairs

all_genres <- unique(books_dt$genreA)
all_pairs <- combn(all_genres, 2, simplify = F)
pair_customers <- 
pair_dt <- data.table(
    genre_pairs = sapply(all_pairs, function(p){
        paste(sort(p), collapse = "&")}
    ),
    pair_customers = sapply(all_pairs, function(p){
        books_dt[genreA==p[1] & genreB==p[2], customers]
    })
)
plot_ly(data=pair_dt[order(pair_customers, decreasing=T)][1:10], type='bar', 
    x=~genre_pairs, y=~pair_customers
)%>% layout(
    margin=list(b=100), 
    xaxis=list(categoryorder="trace"),
    title="Top 10 genre pairs"
)
  • mostly combinations of most bought genres

Special genres

Hypothesis

  • If a customer buys more than 2 genres, he is recorded in more than 1 off-diagonal entry:
    • (2*diagonal - colSum) < 0
  • If a genre is bought more often alone than in triplets (or higher):
    • (2*diagonal - colSum) > 0

Look for customers that buy only one genre

  • Compare column sum and 2*diagonal value
  • generate table with {genre, {2*diagonal-colSum}}
all_genres <- unique(books_dt$genreA)
selective_dt <- data.table()
for(g in all_genres){
    d <- books_dt[genreA==g & genreB==g, customers]
    cs <- sum(books_dt[genreA==g, customers])
    dd <- I(2*d - cs)
    selective_dt <- rbind(selective_dt, data.table(genre=g, diag_diff=dd))
}
p_sel <- plot_ly(
    data=selective_dt[order(diag_diff)], 
    y=~genre, x=~diag_diff, type="bar", 
    color = ~diag_diff>0, colors=c("gray", "darkgreen")
)%>% layout(
    margin=list(l=100), 
    yaxis=list(categoryorder="trace", title=''),
    xaxis=list(title='2*diagonal - columnSum'),
    title="Which genres are bought alone?"
)
show(p_sel)
  • Mystery and Horror are mostly bought alone
  • Satire and Travel rather bought in pairs

Normalize columns by diagonal

books_dt[,
    rel_customers:= (customers/books_dt[genreA==genreB, customers]), 
    by=genreB
    ]
head(books_dt[order(genreA)])

–> genreB relative to genreA-diagonal value

Look at all data unsorted: No pattern.

plot_ly(data=books_dt) %>%
    add_heatmap(
        z=~rel_customers, x=~genreA, y=~genreB, colors= c('grey95', 'dodgerblue')
    ) %>%
    layout(
        margin=list(b=110, l=110)
    )

With clustering of rows and columns (Note: they are different now):

  • 2 hubs on genreA axis (top dendro)
    • Art, Journals, Action, SciFi, History
    • Encyclopedias, Comics, Disctionaries, Poetry, Math, Anthology
    • e.g. genres that were bought with Art were also bought together with Journals
  • 2 hubs on genreB axis (right dendro)
    • Romance, History, Action, SciFi –> Romance instead of Art and Journals
    • same
  • bought with everything else? Romance

Most favorite partner genre

–> about 20% customers additionally bought SciFi and Romance

Relative best pairs

–> Math is poetry and History is Science fiction!

LS0tCnRpdGxlOiAiQWxsaWFueiBEYXRhVml6IENoYWxsZW5nZSIKYXV0aG9yOiAiRGFuaWVsIEJhZGVyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UfQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGhlYXRtYXBseSkKYGBgCgoKYGBge3IsIGVjaG89RkFMU0V9Cm9wdHNfY2h1bmskc2V0KGVjaG89RkFMU0UsIGNhY2hlPUYpCnRvdGFsX2N1c3RvbWVycyA8LSAxOTUzODcKZmlsZV9ib29rc3RvcmUgPC0gZmlsZS5wYXRoKCJ+L0Rvd25sb2Fkcy90b3lkYXRhL2Jvb2tfZ2VucmVzX2RhdGEuY3N2IikKc291cmNlKCJidWlsZF9ib29rX3N0b3JlLlIiKQpgYGAKCgojIFByZXByb2Nlc3NpbmcKCiogTG9hZCBkYXRhIGZpbGUKKiByZW5hbWUgZ2VucmVzIGZvciBiZXR0ZXIgcmVhZGFiaWxpdHkKICAgICogIlJlbGlnaW9uLCBTcGlyaXR1YWxpdHkgJiBOZXcgQWdlIiB0byAiUmVsaWdpb24iCiAgICAqICJTY2llbmNlLmZpY3Rpb24iIHRvICJTY2lGaSIKICAgICogIkFjdGlvbi5hbmQuQWR2ZW50dXJlIiB0byAiQWN0aW9uIgogICAgCkFsbCBnZW5yZXM6CmBgYHtyfQpib29rc19tYXQgPC0gcmVhZC5jc3YoZmlsZV9ib29rc3RvcmUsIHJvdy5uYW1lcyA9IDEpCnJvd25hbWVzKGJvb2tzX21hdCkgPC0gbWFrZS5uYW1lcyhyb3duYW1lcyhib29rc19tYXQpKQpyb3duYW1lcyhib29rc19tYXQpIDwtIHN1YigiU2NpZW5jZS5maWN0aW9uIiwgIlNjaUZpIiwgcm93bmFtZXMoYm9va3NfbWF0KSkKcm93bmFtZXMoYm9va3NfbWF0KSA8LSBzdWIoIkFjdGlvbi5hbmQuQWR2ZW50dXJlIiwgIkFjdGlvbiIsIHJvd25hbWVzKGJvb2tzX21hdCkpCnJvd25hbWVzKGJvb2tzX21hdCkgPC0gc3ViKCJSZWxpZ2lvbi4uU3Bpcml0dWFsaXR5Li4uTmV3LkFnZSIsIAogICAgIlJlbGlnaW9uIiwgcm93bmFtZXMoYm9va3NfbWF0KQopCmNvbG5hbWVzKGJvb2tzX21hdCkgPC0gcm93bmFtZXMoYm9va3NfbWF0KQpyb3duYW1lcyhib29rc19tYXQpCmBgYAoKKiBDaGVjayBpZiB1cHBlciBhbmQgbG93ZXIgdHJpYW5nbGUgaWRlbnRpY2FsCgpgYGB7cn0KaXNfdXBwZXJfbG93ZXIgPC0gaWRlbnRpY2FsKAogICAgYm9va3NfbWF0W3VwcGVyLnRyaShib29rc19tYXQpXSwgCiAgICB0KGJvb2tzX21hdClbdXBwZXIudHJpKGJvb2tzX21hdCldCikKaXNfdXBwZXJfbG93ZXIKYGBgCgoqIFRyYW5zZm9ybSB0byBsb25nIGFuZCB0aWR5IGBkYXRhLnRhYmxlYAoKYGBge3J9CmJvb2tzX2R0IDwtIGFzLmRhdGEudGFibGUoYm9va3NfbWF0LCBrZWVwLnJvd25hbWVzID0gVFJVRSkKc2V0bmFtZXMoYm9va3NfZHQsIGMoJ2dlbnJlQScsY29sbmFtZXMoYm9va3NfbWF0KSkpCmJvb2tzX2R0IDwtIGFzLmRhdGEudGFibGUoZ2F0aGVyKGJvb2tzX2R0LCBnZW5yZUIsIGN1c3RvbWVycywgU2F0aXJlOkpvdXJuYWxzKSkKYGBgCgpgYGB7ciwgZWNobz1UfQpoZWFkKGJvb2tzX2R0KQpgYGAKCgoqIEF2ZXJhZ2UgbnVtYmVyIG9mIGdlbnJlcyBwZXIgY3VzdG9tZXIKCmBgYHtyfQpzdW0oYm9va3NfZHRbZ2VucmVBPT1nZW5yZUIsIGN1c3RvbWVyc10pL3RvdGFsX2N1c3RvbWVycwpgYGAKCgojIEZpcnN0IGlkZWFzCgojIyBTaG93IG1lIGV2ZXJ5dGhpbmchCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KaG0gPC0gaGVhdG1hcHIoYm9va3NfbWF0KQpoZWF0bWFwbHkoaG0sIAogICAgcGxvdF9tZXRob2QgPSAncGxvdGx5JywgCiAgICBjb2xvcnMgPSAgYygnZ3JleTk1JywgJ2RvZGdlcmJsdWUnKQopCmBgYAoKKiBSb21hbmNlLCBTY2lGaSwgQWN0aW9uLCBIaXN0b3J5IGFyZSBtb3N0IGJvdWdodCAKKiBib3VnaHQtdG9nZXRoZXIgY2x1c3RlcnM6CiAgICAqIFJvbWFuY2UsIFNjaUZpLCBBY3Rpb24sIEhpc3RvcnkKICAgICogRGljdGlvbmFyaWVzIGFuZCBDb21pY3MKICAgICogTWF0aCBhbmQgUG9ldHJ5CiogTXlzdGVyeSBpcyBhbiBvdXRsaWVyCgojIyBNb3N0IGJvdWdodCBnZW5yZQoKYGBge3J9CnBsb3RfbHkoZGF0YT1ib29rc19kdFtnZW5yZUE9PWdlbnJlQl1bb3JkZXIoY3VzdG9tZXJzKV0sIAogICAgeD1+Z2VucmVBLCB5PX5jdXN0b21lcnMsIHR5cGU9ImJhciIKKSU+JSBsYXlvdXQoCiAgICBtYXJnaW49bGlzdChiPTEwMCksIAogICAgeGF4aXM9bGlzdChjYXRlZ29yeW9yZGVyPSJ0cmFjZSIpLAogICAgdGl0bGU9Ik1vc3QgYm91Z2h0IGdlbnJlIgopCmBgYAoKIyMgQmVzdCBwYWlycwoKYGBge3J9CmFsbF9nZW5yZXMgPC0gdW5pcXVlKGJvb2tzX2R0JGdlbnJlQSkKYWxsX3BhaXJzIDwtIGNvbWJuKGFsbF9nZW5yZXMsIDIsIHNpbXBsaWZ5ID0gRikKcGFpcl9jdXN0b21lcnMgPC0gCnBhaXJfZHQgPC0gZGF0YS50YWJsZSgKICAgIGdlbnJlX3BhaXJzID0gc2FwcGx5KGFsbF9wYWlycywgZnVuY3Rpb24ocCl7CiAgICAgICAgcGFzdGUoc29ydChwKSwgY29sbGFwc2UgPSAiJiIpfQogICAgKSwKICAgIHBhaXJfY3VzdG9tZXJzID0gc2FwcGx5KGFsbF9wYWlycywgZnVuY3Rpb24ocCl7CiAgICAgICAgYm9va3NfZHRbZ2VucmVBPT1wWzFdICYgZ2VucmVCPT1wWzJdLCBjdXN0b21lcnNdCiAgICB9KQopCnBsb3RfbHkoZGF0YT1wYWlyX2R0W29yZGVyKHBhaXJfY3VzdG9tZXJzLCBkZWNyZWFzaW5nPVQpXVsxOjEwXSwgdHlwZT0nYmFyJywgCiAgICB4PX5nZW5yZV9wYWlycywgeT1+cGFpcl9jdXN0b21lcnMKKSU+JSBsYXlvdXQoCiAgICBtYXJnaW49bGlzdChiPTEwMCksIAogICAgeGF4aXM9bGlzdChjYXRlZ29yeW9yZGVyPSJ0cmFjZSIpLAogICAgdGl0bGU9IlRvcCAxMCBnZW5yZSBwYWlycyIKKQpgYGAKCiogbW9zdGx5IGNvbWJpbmF0aW9ucyBvZiBtb3N0IGJvdWdodCBnZW5yZXMKCgojIFNwZWNpYWwgZ2VucmVzCgpIeXBvdGhlc2lzCgoqIElmIGEgY3VzdG9tZXIgYnV5cyBtb3JlIHRoYW4gMiBnZW5yZXMsIApoZSBpcyByZWNvcmRlZCBpbiBtb3JlIHRoYW4gMSBvZmYtZGlhZ29uYWwgZW50cnk6CiAgICAqICgyKmRpYWdvbmFsIC0gY29sU3VtKSA8IDAKKiBJZiBhIGdlbnJlIGlzIGJvdWdodCBtb3JlIG9mdGVuIGFsb25lIHRoYW4gaW4gdHJpcGxldHMgKG9yIGhpZ2hlcik6IAogICAgKiAoMipkaWFnb25hbCAtIGNvbFN1bSkgPiAwCgoKTG9vayBmb3IgY3VzdG9tZXJzIHRoYXQgYnV5IG9ubHkgb25lIGdlbnJlCgoqIENvbXBhcmUgYGNvbHVtbiBzdW1gIGFuZCAgYDIqZGlhZ29uYWwgdmFsdWVgCiogZ2VuZXJhdGUgdGFibGUgd2l0aCBge2dlbnJlLCB7MipkaWFnb25hbC1jb2xTdW19fWAKCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTh9CmFsbF9nZW5yZXMgPC0gdW5pcXVlKGJvb2tzX2R0JGdlbnJlQSkKc2VsZWN0aXZlX2R0IDwtIGRhdGEudGFibGUoKQpmb3IoZyBpbiBhbGxfZ2VucmVzKXsKICAgIGQgPC0gYm9va3NfZHRbZ2VucmVBPT1nICYgZ2VucmVCPT1nLCBjdXN0b21lcnNdCiAgICBjcyA8LSBzdW0oYm9va3NfZHRbZ2VucmVBPT1nLCBjdXN0b21lcnNdKQogICAgZGQgPC0gSSgyKmQgLSBjcykKICAgIHNlbGVjdGl2ZV9kdCA8LSByYmluZChzZWxlY3RpdmVfZHQsIGRhdGEudGFibGUoZ2VucmU9ZywgZGlhZ19kaWZmPWRkKSkKfQoKcF9zZWwgPC0gcGxvdF9seSgKICAgIGRhdGE9c2VsZWN0aXZlX2R0W29yZGVyKGRpYWdfZGlmZildLCAKICAgIHk9fmdlbnJlLCB4PX5kaWFnX2RpZmYsIHR5cGU9ImJhciIsIAogICAgY29sb3IgPSB+ZGlhZ19kaWZmPjAsIGNvbG9ycz1jKCJncmF5IiwgImRhcmtncmVlbiIpCiklPiUgbGF5b3V0KAogICAgbWFyZ2luPWxpc3QobD0xMDApLCAKICAgIHlheGlzPWxpc3QoY2F0ZWdvcnlvcmRlcj0idHJhY2UiLCB0aXRsZT0nJyksCiAgICB4YXhpcz1saXN0KHRpdGxlPScyKmRpYWdvbmFsIC0gY29sdW1uU3VtJyksCiAgICB0aXRsZT0iV2hpY2ggZ2VucmVzIGFyZSBib3VnaHQgYWxvbmU/IgopCgpzaG93KHBfc2VsKQpgYGAKKiBNeXN0ZXJ5IGFuZCBIb3Jyb3IgYXJlIG1vc3RseSBib3VnaHQgYWxvbmUKKiBTYXRpcmUgYW5kIFRyYXZlbCByYXRoZXIgYm91Z2h0IGluIHBhaXJzCgoKCiMgTm9ybWFsaXplIGNvbHVtbnMgYnkgZGlhZ29uYWwKCmBgYHtyfQpib29rc19kdFssCiAgICByZWxfY3VzdG9tZXJzOj0gKGN1c3RvbWVycy9ib29rc19kdFtnZW5yZUE9PWdlbnJlQiwgY3VzdG9tZXJzXSksIAogICAgYnk9Z2VucmVCCiAgICBdCmhlYWQoYm9va3NfZHRbb3JkZXIoZ2VucmVBKV0pCmBgYAoKLS0+IGdlbnJlQiByZWxhdGl2ZSB0byBnZW5yZUEtZGlhZ29uYWwgdmFsdWUgCgpMb29rIGF0IGFsbCBkYXRhIHVuc29ydGVkOiBObyBwYXR0ZXJuLgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnBsb3RfbHkoZGF0YT1ib29rc19kdCkgJT4lCiAgICBhZGRfaGVhdG1hcCgKICAgICAgICB6PX5yZWxfY3VzdG9tZXJzLCB4PX5nZW5yZUEsIHk9fmdlbnJlQiwgY29sb3JzPSBjKCdncmV5OTUnLCAnZG9kZ2VyYmx1ZScpCiAgICApICU+JQogICAgbGF5b3V0KAogICAgICAgIG1hcmdpbj1saXN0KGI9MTEwLCBsPTExMCkKICAgICkKYGBgCgpXaXRoIGNsdXN0ZXJpbmcgb2Ygcm93cyBhbmQgY29sdW1ucyAoTm90ZTogdGhleSBhcmUgZGlmZmVyZW50IG5vdyk6CgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KYm9va3NfcmVsbWF0IDwtIGRjYXN0KGJvb2tzX2R0LCBnZW5yZUEgfiBnZW5yZUIsIHZhbHVlLnZhciA9ICJyZWxfY3VzdG9tZXJzIikKYm9va3NfcmVsbWF0IDwtIGFzLm1hdHJpeChib29rc19yZWxtYXRbLGdlbnJlQTo9TlVMTF0pCnJvd25hbWVzKGJvb2tzX3JlbG1hdCkgPC0gY29sbmFtZXMoYm9va3NfcmVsbWF0KQoKaG1yZWwgPC0gaGVhdG1hcHIodChib29rc19yZWxtYXQpLCBrX2NvbD0zLCBrX3Jvdz0zKQpoZWF0bWFwbHkoCiAgICB4PWhtcmVsLCAKICAgIHBsb3RfbWV0aG9kID0gJ3Bsb3RseScsIAogICAgY29sb3JzID0gIGMoJ2dyZXk5NScsICdkb2RnZXJibHVlJyksCiAgICB4bGFiPSdnZW5yZUEnLCB5bGFiPSdnZW5yZUInCikgJT4lIGxheW91dCgKICAgIHRpdGxlPSdDdXN0b21lcnMgb2YgZ2VucmVCIHJlbGF0aXZlIHRvIGdlbnJlQScsCiAgICBtYXJnaW49bGlzdCh0PTUwKQopCmBgYAoKKiAyIGh1YnMgb24gZ2VucmVBIGF4aXMgKHRvcCBkZW5kcm8pCiAgICAqIEFydCwgSm91cm5hbHMsIEFjdGlvbiwgU2NpRmksIEhpc3RvcnkKICAgICogRW5jeWNsb3BlZGlhcywgQ29taWNzLCBEaXNjdGlvbmFyaWVzLCBQb2V0cnksIE1hdGgsIEFudGhvbG9neQogICAgKiBlLmcuIGdlbnJlcyB0aGF0IHdlcmUgYm91Z2h0IHdpdGggQXJ0IHdlcmUgYWxzbyBib3VnaHQgdG9nZXRoZXIgd2l0aCBKb3VybmFscwoqIDIgaHVicyBvbiBnZW5yZUIgYXhpcyAocmlnaHQgZGVuZHJvKQogICAgKiBSb21hbmNlLCBIaXN0b3J5LCBBY3Rpb24sIFNjaUZpIC0tPiBSb21hbmNlIGluc3RlYWQgb2YgQXJ0IGFuZCBKb3VybmFscwogICAgKiBzYW1lCiogYm91Z2h0IHdpdGggZXZlcnl0aGluZyBlbHNlPyBSb21hbmNlCgoKIyMgTW9zdCBmYXZvcml0ZSBwYXJ0bmVyIGdlbnJlCgpgYGB7cn0KcGxvdF9seSgKICAgIGRhdGE9Ym9va3NfZHRbLG1lZGlhbihyZWxfY3VzdG9tZXJzKSwgYnk9Z2VucmVCXVtvcmRlcihWMSwgZGVjcmVhc2luZyA9IFQpXQogICAgKSU+JQogICAgYWRkX2JhcnMoeD1+Z2VucmVCLCB5PX5WMSklPiUKICAgIGxheW91dCgKICAgICAgICB5YXhpcz1saXN0KHRpdGxlPSdNZWRpYW4gcmVsYXRpdmUgY3VzdG9tZXJzJyksCiAgICAgICAgeGF4aXM9bGlzdChjYXRlZ29yeW9yZGVyPSd0cmFjZScpLAogICAgICAgIG1hcmdpbj1saXN0KGI9MTAwKQogICAgKQpgYGAKCi0tPiBhYm91dCAyMCUgY3VzdG9tZXJzIGFkZGl0aW9uYWxseSBib3VnaHQgU2NpRmkgYW5kIFJvbWFuY2UKCgojIyBSZWxhdGl2ZSBiZXN0IHBhaXJzCgpgYGB7cn0KcGxvdF9seSgKICAgICAgICBkYXRhID0gYm9va3NfZHRbZ2VucmVBICE9IGdlbnJlQl1bb3JkZXIocmVsX2N1c3RvbWVycywgZGVjcmVhc2luZyA9IFQpXVsxOjEwXQogICAgKSAlPiUgCiAgICBhZGRfYmFycygKICAgICAgICB4PX5wYXN0ZTAoZ2VucmVBLCAiJiIsIGdlbnJlQiksIHk9fnJlbF9jdXN0b21lcnMKICAgICAgICApICU+JSAKICAgIGxheW91dCgKICAgICAgICBtYXJnaW49bGlzdChiPTEwMCwgcj04MCksIAogICAgICAgIHhheGlzPWxpc3QoY2F0ZWdvcnlvcmRlcj0idHJhY2UiLCB0aXRsZT0nJyksCiAgICAgICAgeWF4aXM9bGlzdChleHBvbmVudGZvcm1hdD0nbm9uZScpLAogICAgICAgIHRpdGxlPSJUb3AgMTAgcmVsYXRpdmUgZ2VucmUgcGFpcnMiCikKYGBgCgotLT4gKipNYXRoIGlzIHBvZXRyeSBhbmQgSGlzdG9yeSBpcyBTY2llbmNlIGZpY3Rpb24hKioK